package com.je.mongodb.config;

import com.google.common.base.Strings;
import com.mongodb.ClientSessionOptions;
import com.mongodb.DB;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

//@Configuration
public class MongoDbConfig {

    private static String DATEBASENAME = "";

    @Bean
    public MongoClient mongo() throws IOException {
        Properties mongoProps = PropertiesLoaderUtils.loadAllProperties("mongo.properties");
        if (mongoProps == null) {
            return null;
        }
        String userName = mongoProps.getProperty("mongodb.username");
        if (Strings.isNullOrEmpty(userName)) {
            return null;
        }
        String password = mongoProps.getProperty("mongodb.password");
        String databasename = mongoProps.getProperty("mongodb.databasename");
        String repSet = mongoProps.getProperty("mongodbs.replicaSet");
        if (!Strings.isNullOrEmpty(repSet) && repSet.equals("1")) {
            String host = mongoProps.getProperty("mongodbs.hosts");
            String replicaSet = mongoProps.getProperty("mongodbs.replica-set");
            return MongoClients.create(String.format("mongodb://%s:%s@%s/%s", userName, password, host, databasename));
        } else {
            String host = mongoProps.getProperty("mongodb.host");
            String port = mongoProps.getProperty("mongodb.port");
            return MongoClients.create(String.format("mongodb://%s:%s@%s:%s/?authSource=%s", userName, password, host, port, databasename));
        }
    }

    @Bean
    public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory) {
        // 还有其它的初始化方法。
        return new MongoTemplate(mongoDbFactory);
    }

    @Bean
    public MongoDbFactory withSession(MongoClient mongoClient) {
        return new MongoDbFactory() {
            @Override
            public MongoDatabase getDb() throws DataAccessException {
                if (Strings.isNullOrEmpty(DATEBASENAME)) {
                    Properties mongoProps = null;
                    try {
                        mongoProps = PropertiesLoaderUtils.loadAllProperties("mongo.properties");
                        DATEBASENAME = mongoProps.getProperty("mongodb.databasename");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                MongoDatabase mongoDatabase = mongoClient.getDatabase(DATEBASENAME);
                return mongoDatabase;
            }

            @Override
            public MongoDatabase getDb(String s) throws DataAccessException {
                return mongoClient.getDatabase(s);
            }

            @Override
            public PersistenceExceptionTranslator getExceptionTranslator() {
                return null;
            }

            @Override
            public DB getLegacyDb() {
                return null;
            }

            @Override
            public ClientSession getSession(ClientSessionOptions options) {
                return null;
            }

            @Override
            public MongoDbFactory withSession(ClientSession clientSession) {
                return null;
            }

        };
    }

    @Bean
    public GridFsTemplate gridFsTemplate(MongoDbFactory mongoDatabaseFactory, MongoConverter converter) {
        return new GridFsTemplate(mongoDatabaseFactory, converter);
    }


    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDbFactory mongoDatabaseFactory) throws Exception {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext());
        return converter;
    }


    @Bean
    public MongoMappingContext mongoMappingContext() throws ClassNotFoundException {
        MongoMappingContext mappingContext = new MongoMappingContext();
        mappingContext.setInitialEntitySet(getInitialEntitySet());
        mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
        return mappingContext;
    }

    protected FieldNamingStrategy fieldNamingStrategy() {
        return abbreviateFieldNames() ? new CamelCaseAbbreviatingFieldNamingStrategy()
                : PropertyNameFieldNamingStrategy.INSTANCE;
    }

    protected boolean abbreviateFieldNames() {
        return false;
    }


//    @Bean
//    public CustomConversions customConversions() {
//        CustomConversions customConversions = new CustomConversions(CustomConversions.StoreConversions, Collection<?> converters);
//        return customConversions;
//    }


    /**
     * Scans the mapping base package for classes annotated with {@link Document}.
     *
     * @return
     * @throws ClassNotFoundException
     */
    protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {

        String basePackage = getMappingBasePackage();
        Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
        if (StringUtils.hasText(basePackage)) {
            ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
                    false);
            componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
            componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));

            for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
                initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
                        AbstractMongoConfiguration.class.getClassLoader()));
            }
        }

        return initialEntitySet;
    }

    protected String getMappingBasePackage() {
        Package mappingBasePackage = getClass().getPackage();
        return mappingBasePackage == null ? null : mappingBasePackage.getName();
    }

//    @Bean
//    public GridFSBucket getGridFSBucket(MongoClient mongoClient, StandardEnvironment env) {
//        return GridFSBuckets.create(mongoClient.getDatabase(env.getProperty("mongodb.databasename")));
//    }


}
